home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacFormat 1996 January
/
macformat-033.iso
/
mac
/
Shareware City
/
Developers
/
VideoToolbox
/
VideoToolboxSources
/
Assign.c
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS
UTF-8
Wrap
Text File
|
1995-11-08
|
58.7 KB
|
2,050 lines
|
[
TEXT/CWIE
]
/*
Assign.c
Assign is a portable runtime C interpreter that reads and executes any text
"assignment" file that contains only C assignments and comments, e.g.
viewingDistance=57.0; // inches
See Assign.note for documentation.
(It would be nice to add a flag for PrintAssignmentsToFile() that would request
a read-after-write check, ala WriteLuminanceRecord in ReadLuminanceRecord.c.)
HISTORY
7/30/91 dgp wrote it as "ReadAssignments.c".
8/4/91 dgp added new routines and renamed the old ones. Everything
seems to work, but has not been thoroughly tested.
8/5/91 dgp MPW C 3.2 now compiles it without error messages. The MPW C macro processor,
contrary to Standard C, finds comment symbols inside strings and finds
preprocessor directives even when # is not the first nonblank character.
8/24/91 dgp Made compatible with THINK C 5.0.
Changed ReadAssignment file to return an error code instead of aborting
if it can't open the file.
8/26/91 dgp Added SetVariable() and noted, in Assign.note, that the Variable array
is terminated by an element with its "type" field set to zero. (Thanks
to Evan Relkin for pointing out the omission.)
4/1/92 dgp Tidied up Assign.note.
4/2/92 dgp Introduced ReadAssignmentBlock() and AssignmentLineWasBlank().
Deleted ReadAssignments().
4/5/92 dgp ReadAssignmentFile() now closes the file before returning.
4/17/92 dgp Expanded the explanation of flags, as requested by Evan Relkin.
5/14/92 dgp Expanded the explanation of stringType, "".
8/4/92 dgp Added PrintAnAssignment(), which prints out the value of a variable,
as an assignment statement, suitable for reading by the ReadAssignment
routines.
10/24/92 dgp Eliminated double spacing that occurred after //-style comment when
assignEchoComment was requested.
3/4/93 dgp changed the definition of emptyString slightly so that this file could be
compiled as a code resource.
5/24/93 dgp Assignment lines may now be continued by putting a backslash at the end of
the line.
Strings may include literal linefeeds, rather than just "\n".
Printed string assignments translate linefeeds to "\n".
Skip unknown variables unless flags&assignReportUnknown is true.
5/27/93 dgp Added "dim" field to Variable structure, and added support for
it in all the routines.
Added floatType.
Input line length is still limited to BUFFER_SIZE (presently 512) but
strings (broken up into lines of legal length and automatically
concatenated during reading) can be of any length that will fit
in memory.
5/28/93 dgp Added support for hex-encoded strings.
Created new bottleneck procedure ReadLine that does all the reading.
5/31/93 dgp assignEchoAssignments now calls PrintAnAssignment. Removed "\n" from
PrintAnAssignment. Made all Print routines return assignment count,
which is now redefined as the number of array elements or scalars
that were assigned or printed. Now parse names so that
known and unknown identifiers are treated uniformly; these new
naming rules allow only simple C identifiers joined by "." and "->".
6/1/93 dgp introduced assignNoPrintfExit flag that causes all routines to handle
errors discreetely, simply returning an error number to the caller.
6/2/93 dgp PrintAnAssignment now prints multiple assignments per 80 character line,
adding as many continuation lines as necessary. Quoted strings (and hex
data strings) are now broken up into 80-character lines; continuation
lines are indented four spaces. Added comment field to Variable structure
and added support for it in PrintAnAssignment. Renamed all published
enum values to begin with "assign". Renamed this file "Assign.c". Added
UnequalVariables(). CheckAVariable checks that variable name is legal.
6/5/93 dgp Removed all Macintosh dependencies, leaving pure Standard C plus a few
C++ style comments.
6/16/93 dgp Removed prohibition: arrays of stringType are now ok.
InitializeAVariable now calls CheckAVariable with "flags" unmodified.
CheckAVariable now checks for any illegal suffix in name.
Unknown subscripted variables are now correctly reported as "UNKNOWN"
rather than "OUT OF BOUNDS".
6/29/93 dgp added shortDoubleType.
7/10/93 dgp restored Standard C compatibility by using "short double" only if
SHORT_DOUBLE_OK, since Standard C doesn't allow that type.
7/19/93 dgp noted that assign file should be opened in text mode, not binary mode, so that
\r characters will be translated properly.
7/29/93 dhb & dgp Found and fixed the newline problem that appears in the MATLAB
environment, as documented in Assign.note.
7/31/93 dgp Added support for multidimensional arrays, with up to ASSIGN_DIMS
dimensions. Added SetVariableArray(), changed SetVariable() and
SetVariableFirstLast(), and deleted SetAVariableFirstLast().
8/2/93 dgp Added dynamic allocation of ptrType arrays, based on scanning the current
gulp of the assign file.
8/12/93 dgp Renamed "Variable" to "Description". Renamed most of the routines.
The header file Assign92.h provides for backward compatibility.
8/21/93 dhb,dgp For compatibility with MATLAB we avoid using int arguments in stdio
calls because when MATLAB is true we'll be using MPW's stdio
library, and its int is 4 bytes whereas the THINK C int is 2 bytes.
dhb,jms Changed "wa" in fopen in PrintAssignmentsToFile to "a".
dhb,jms,dgp Moved the NL and NEWLINE macros into VideoToolbox.h.
9/2/93 dgp Expanded documentation of FreePtrVariables. Fixed minor bugs
in PrintAVariable for hex encoded Ptr variables so that dimensionality
of what was written is recovered when it's read.
9/7/93 dgp Introduced STDIO_INT.
9/7/93 dhb,jms,dgp Added OpenCalFileWrite, OpenCalFileRead, OpenCalFileReadAndCheck,
AppendDescriptions, CopyDescriptions, AllocateDescriptions,
FreeDescriptions, NumberOfDescriptions, NullDescription, and
IsNullDescription.
9/9/93 dhb Added ReadAssignmentStream, to read the rest of a file stream as a single
gulp.
9/11/93 dgp rewrote ReadAssignmentFile to just open the file and call
ReadAssignmentStream.
9/12/93 dhb Changed STDIO_INT to PRINTF_INT, added inclusion of VideoToolbox
when MATLAB is on.
9/15/93 dgp Moved documentation to Assign.note.
9/16/93 dgp Merged Assign.h into VideoToolbox.h.
3/4/94 dgp Increased number of float and double digits produced by PrintAnAssignment
up to that specified in float.h. This should eliminate spurious failure
of the read-back checks.
Enhanced PrintQuotedString to correctly handle strings containing the double
quote character.
3/28/94 dgp Inserted space before comments (i.e. before "/*") and now pad the inside
of the comment delimiters with option-space instead of space for more
pleasing word wrapping when the assignment file is viewed in that way.
6/16/94 dgp Enhanced ParsingError to use PrintQuotedString to print the offending text
so that any spurious nonprinting characters will be revealed.
ParsingError now suppresses "\n" at end of offending string.
Enhanced PrintQuotedString to accept arbitrary open and close strings,
typically just the traditional double-quote character.
6/17/94 dgp Modified PrintQuotedString to print option-space "\312" directly, so
that the printout will be readable.
7/6/94 dgp Replaced maximum linelength of 80 by a compile-time constant of 76, so
that assignment files can be emailed.
7/7/94 dgp Added CopyDescribedVars.
7/11/94 dgp Fixed bug in ReadAssignmentStream and ReadAssignmentLine that garbled the
returned error value, if there was an error, by adding to it the number of
lines read.
7/16/94 dgp Found and fixed a memory leak in ReadAssignmentLine. GetName() called
malloc for dScratch->name, which wasn't being freed. This cost about
40 bytes for every variable. I fixed that. There still seems to be a
much smaller memory leak, which I couldn't quickly locate, and which
I can live with for the time being.
9/5/94 dgp added support for reading of Metrowerks CodeWarrior C style of printing
NANs, e.g. NAN(021)
9/10/94 dgp the variable "error" is now always declared int. (So it's safe to print with %d.)
7/19/95 dgp made compatible with Standard C by changing // to standard C comments.
11/8/95 dgp tracked down and fixed bug in ReadAssignmentLine(), the Description was being corrupted
by freeing an unallocated variable. The fix is marked by "11/8/95".
*/
#include "VideoToolbox.h"
#include <stdarg.h>
/*
#include <assert.h> // Already included by VideoToolbox.h
#include <ctype.h> // Already included by VideoToolbox.h
#include <stdio.h> // Already included by VideoToolbox.h
#include <stdlib.h> // Already included by VideoToolbox.h
#include <string.h> // Already included by VideoToolbox.h
*/
#define MAX_LINELENGTH 76
/*
IsNan and IsFinite--see VideoToolbox.h--should work on most Intel and Motorola
processors. If IsNan and IsFinite don't work on your computer, don't fret. They
are used solely by UnequalDescribedVarPair. A quick fix is to use the following
definitions, the only bad effect of which will be that comparisons of NANs may
appear spuriously unequal.
*/
#ifndef IsFinite
#define IsNan(x) 0
#define IsFinite(x) 1
#endif
Description SetAVariable(short type,void *ptr,char *name,unsigned long dim
,const char *comment); /* old style */
/* the following are private, not intended for use outside this file */
#define IsIntegral(type) ((type)>=charType&&(type)<=unsignedLongType || (type)>=charPtrType&&(type)<=unsignedLongPtrType)
#define IsFloating(type) ((type)>=floatType&&(type)<=doubleType || (type)>=floatPtrType&&(type)<=doublePtrType)
#define IsString(type) (type==stringType)
#define IsPtr(type) ((type)>=charPtrType&&(type)<=doublePtrType)
long CheckADescription(Description *d,const char *here,short flags);
long CheckDescriptions(Description d[],const char *here,short flags);
void FreeAPtrDescription(Description *d,short flags);
void FreeAStringDescription(Description *d,short flags);
char *GetName(char **sPtr,short flags);
char *GetQuotedString(FILE *stream,char *lineBuffer,char *s,char **sPtr,short flags);
long HexAssignment(FILE *stream,char *lineBuffer,char **sPtr,Description *d
,int subscriptDims,long index,long *bytesPtr,short flags);
char *NextToken(FILE *stream,char *lineBuffer,char *s,short flags);
char *NextTokenInLine(FILE *stream,char *lineBuffer,char *s,short flags);
char *NextTokenInThisOrNextLine(FILE *stream,char *lineBuffer,char *s,short flags);
int ParseName(char **sPtr);
void ParsingError(char *s,char *format,...);
long PrintAnAssignmentOfElement(FILE *stream,Description *d,long index,short flags);
void PrintQuotedString(FILE *stream,short *lineLengthPtr,const char *s
,const char *openQuote,const char *closeQuote);
char *ReadLine(char *lineBuffer,size_t bufferSize,FILE *stream,short flags);
double strtodN(char *s,char **sPtr);
long strtolN(char *s,char **sPtr,short flags);
unsigned long strtoulN(char *s,char **sPtr,short flags);
char strtoc(char *s,char **sPtr);
#define streq2(s1,s2) (strncmp(s1,s2,strlen(s2))==0)
#define streq(s1,s2) (strcmp(s1,s2)==0)
long Dims(Description *d);
char *ElementName(char *s,Description *d,long index);
void *ElementPtr(Description *d,long index);
long Elements(Description *d);
void ElementSubscript(Description *d,long index,long sub[ASSIGN_DIMS]);
long ElementIndex(Description *d,long sub[ASSIGN_DIMS]);
int GetDimensions(Description *d,int subscriptDims,long subscript[ASSIGN_DIMS],short flags);
int AllocateDescribedPtrVars(Description description[],const char *here,short flags);
#ifndef TRUE
#define FALSE 0
#define TRUE 1
#endif
#define TOLERANCE 1e-6 /* fractional difference between two "equal" floats */
#define BUFFER_SIZE 512
#define ECHO_ASSIGNMENTS (flags&assignEchoAssignments && !(flags&assignScan))
#define ECHO_COMMENTS (flags&assignEchoComments && !(flags&assignScan))
#define ECHO_FILE (flags&assignEchoFile && !(flags&assignScan))
#define PRINTF_EXIT !(flags&assignNoPrintfExit)
static int lineWasBlank=1; /* used in ReadAssignmentLine(), */
/* NextTokenInThisOrNextLine(), and AssignmentLineWasBlank().*/
short typeSize[]={0
,sizeof(char),sizeof(unsigned char)
,sizeof(short),sizeof(unsigned short)
,sizeof(long),sizeof(unsigned long)
,sizeof(float)
#if SHORT_DOUBLE_OK
,sizeof(short double)
#endif
,sizeof(double)
,sizeof(char),sizeof(unsigned char)
,sizeof(short),sizeof(unsigned short)
,sizeof(long),sizeof(unsigned long)
,sizeof(float)
#if SHORT_DOUBLE_OK
,sizeof(short double)
#endif
,sizeof(double)
,sizeof(char *)
,0
};
char typeName[][20]={"none"
,"char","unsigned char","short","unsigned short"
,"long","unsigned long","float"
#if SHORT_DOUBLE_OK
,"short double"
#endif
,"double"
,"char *","unsigned char *","short *","unsigned short *"
,"long *","unsigned long *","float *"
#if SHORT_DOUBLE_OK
,"short double *"
#endif
,"double *"
,"string"
,"unknown"
};
enum{assignNeedMore=0x4000,assignAlreadyChecked=0x2000
,assignScan=0x100,assignLocalCall=0x200
};/*flags used only in this file*/
/*
ROUTINE: OpenCalFileReadAndCheck
PURPOSE:
Open up a calibration file for reading.
If the file exists in the current directory, it is used.
If not, then if it exists in the preferences folder, it used.
*/
FILE *OpenCalFileReadAndCheck(char *filename)
{
FILE *stream;
stream = OpenCalFileRead(filename);
if (stream == NULL)
PrintfExit("OpenCalFileReadAndCheck: cannot open file \"%s\"",filename);
return(stream);
}
/*
ROUTINE: OpenCalFileRead
PURPOSE:
Open up a calibration file for reading. Don't abort on error.
If the file exists in the current directory, it is used.
If not, then if it exists in the preferences folder, it used.
*/
FILE *OpenCalFileRead(char *filename)
{
FILE *stream;
/* Try the current directory */
stream = fopen(filename,"r");
if (stream != NULL) return(stream);
/* Try the preferences folder */
OpenPreferencesFolder();
stream = fopen(filename,"r");
ClosePreferencesFolder();
return(stream);
}
/*
ROUTINE: OpenCalFileWrite
PURPOSE:
Open up a calibration file for appending.
If the file exists in the current directory, it is used.
If not, then if it exists in the Preferences folder, it used.
If not, the file is created in the Preferences folder.
*/
FILE *OpenCalFileWrite(char *filename)
{
FILE *stream;
/* Try in the current directory */
stream = fopen(filename,"r");
if (stream != NULL) {
fclose(stream);
stream = fopen(filename,"a");
if (stream == NULL)
PrintfExit("OpenCalFileWrite: cannot reopen file \"%s\"",filename);
return(stream);
}
/* Try the Preferences folder */
OpenPreferencesFolder();
stream = fopen(filename,"r");
ClosePreferencesFolder();
if (stream != NULL) {
fclose(stream);
OpenPreferencesFolder();
stream = fopen(filename,"a");
ClosePreferencesFolder();
if (stream == NULL)
PrintfExit("OpenCalFileWrite: cannot reopen file \"%s\" in Preferences folder"
,filename);
return(stream);
}
/* Create it in the Preferences folder */
OpenPreferencesFolder();
stream = fopen(filename,"a");
ClosePreferencesFolder();
if (stream == NULL)
PrintfExit("OpenCalFileWrite: cannot create file \"%s\" in Preferences folder.",filename);
return(stream);
}
/*
ROUTINE: AppendDescriptions
PURPOSE:
Appends the second descriptions array onto the end of the first,
which is reallocated with more space.
The source array is not freed; the caller should do that.
*/
void AppendDescriptions(Description **d,Description *s)
{
Description *dTemp;
long n1, n2, n;
n1 = NumberOfDescriptions(*d);
n2 = NumberOfDescriptions(s);
n = n1+n2;
dTemp = AllocateDescriptions(n);
CopyDescriptions(dTemp,*d);
CopyDescriptions(dTemp+n1,s);
FreeDescriptions(*d);
*d=dTemp;
}
/*
ROUTINE: CopyDescriptions
PURPOSE:
Copy one null-terminated array of descriptions to another, which is assumed to
be big enough.
*/
void CopyDescriptions(Description *d,Description *s)
{
long i = 0;
while ( !IsNullDescription(s[i]) ) {
d[i]=s[i];
i++;
}
d[i]=NullDescription();
}
/*
ROUTINE: AllocateDescriptions
PURPOSE:
Allocate space for variable descriptions.
Adds one to the passed size to allow use of null element as end marker.
Nulls all the elements.
*/
Description *AllocateDescriptions(long n)
{
Description *d;
d = calloc(n+1,sizeof(Description));
if(d == NULL)PrintfExit("AllocateDescription: memory allocation failure.");
d[0]=NullDescription();
for(;n>0;n--)d[n]=d[0];
return(d);
}
/*
ROUTINE: FreeDescriptions
PURPOSE:
Free the description space. Does not affect the described variables
themselves.
*/
void FreeDescriptions(Description *d)
{
free(d);
}
/*
ROUTINE: NumberOfDescriptions
PURPOSE:
Find the size of an array of descriptions.
Does not count the trailing null description.
*/
long NumberOfDescriptions(Description *d)
{
long i=0;
while ( !IsNullDescription(d[i]) ) i++;
return(i);
}
/*
ROUTINE: NullDescription
PURPOSE:
Returns a null description.
Standard C specifies that static variables are initialized to zero.
*/
Description NullDescription(void)
{
static Description d;
return d;
}
Description Describe(short type,void *ptr,char *name,const char *comment)
{
static Description var;
var=DescribeArray(type,ptr,name,comment,0L);
if(IsPtr(type)) var.sizedOnce=var.sized=0;
return var;
}
Description DescribeArray(short type,void *ptr,char *name,const char *comment,...)
/* WARNING: the dimensions must be (long) and the last argument must be 0L. */
{
va_list args;
static Description var;
int i;
long dim;
var.name=name;
var.ptr=ptr;
var.type=type;
var.firstElement=0;
for(i=0;i<ASSIGN_DIMS;i++)var.dim[i]=0;
var.comment=comment;
va_start(args,comment);
for(i=0;;i++){
dim=va_arg(args,long);
if(dim==0)break;
if(i>=ASSIGN_DIMS)PrintfExit("Describe/Array/FirstLast: "
"too many dimensions (or missing final 0L) for \"%s\".\n",var.name);
var.dim[i]=dim;
}
va_end(args);
if(var.type!=0 && var.ptr==NULL)
PrintfExit("Describe/Array/FirstLast: \"%s\" ptr is NULL.\n",var.name);
/* Note: we're clearing the malloced flag, which is the only safe assumption. */
var.malloced=0;
var.sizedOnce=var.sized=1;
return var;
}
Description DescribeFirstLast(short type,void *ptr,char *name
,const char *comment,long firstElement,long lastElement)
{
static Description var;
long dim;
dim=1+lastElement-firstElement;
var=DescribeArray(type,ptr,name,comment,dim,0L);
var.firstElement=firstElement;
return var;
}
Description SetAVariable(short type,void *ptr,char *name,unsigned long dim
,const char *comment)
/* Old style, retained solely for compatibility */
{
if(dim==0)return Describe(type,ptr,name,comment);
else return DescribeArray(type,ptr,name,comment,dim,0L);
}
int AllocateDescribedPtrVars(Description description[],const char *here,short flags)
{
register Description *d;
for(d=description;d->type!=0;d++){
if(IsPtr(d->type) && d->sizedOnce && !d->malloced){
assert(d->ptr!=NULL);
*(void **)d->ptr=malloc(Elements(d)*typeSize[d->type]);
if(*(void **)d->ptr==NULL){
if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes for \"%s\".\n"
,here,Elements(d)*typeSize[d->type],d->name);
else return assignMemoryError;
}
d->sized=d->malloced=1;
InitializeADescribedVar(d,flags);
}
}
return 0;
}
long CheckDescriptions(Description d[],const char *here,short flags)
{
int error;
if(flags&assignAlreadyChecked)return 0;
for(;d->type!=0;d++){
error=CheckADescription(d,here,flags);
if(error)return error;
}
return 0;
}
long CheckADescription(Description *d,const char *here,short flags)
{
char *s,*sOk;
if(flags&assignAlreadyChecked)return 0;
if(d->type<=0 || d->type>=unknownType){
if(PRINTF_EXIT)PrintfExit("%s: \"%s\" has unknown type %ld.\n"
,here,d->name,(long)d->type);
else return assignVariableError;
}
if(!IsPtr(d->type) && d->ptr==NULL){
if(PRINTF_EXIT)PrintfExit("%s: \"%s\" ptr is NULL.\n"
,here,d->name);
else return assignVariableError;
}
/* Squeeze any space out of the name */
for(s=sOk=d->name;*s!=0;s++)if(!isspace(*s))*sOk++=*s;
*sOk=0;
/* Check name's syntax */
s=d->name;
if(ParseName(&s) || *s!=0){
if(PRINTF_EXIT)PrintfExit("%s: illegal name \"%s\".\n"
,here,d->name);
else return assignVariableError;
}
return 0;
}
int UnequalDescribedVars(Description d1[],Description d2[],short flags)
/*
Compares the data pointed to by the two Description arrays and returns
true if the structs are legal and the data are equal, false otherwise.
*/
{
int error;
for(;d1->type!=0 && d2->type!=0;d1++,d2++){
error=UnequalDescribedVarPair(d1,d2,flags);
if(error)return error;
}
return 0;
}
int CopyDescribedVars(Description d1[],Description d2[],short flags)
/*
Copies the data pointed to by the first Description array, overwriting the data
pointed to by the second. Returns 0 if successful, i.e. the structs are legal and
the types and names are consistent, nonzero otherwise.
*/
{
int error;
for(;d1->type!=0 && d2->type!=0;d1++,d2++){
error=CopyDescribedVarPair(d1,d2,flags);
if(error)return error;
}
return 0;
}
long Elements(Description *d)
{
long elements=1;
int i;
for(i=0;i<ASSIGN_DIMS && d->dim[i]>0;i++)elements*=d->dim[i];
return elements;
}
long Dims(Description *d)
{
int i;
for(i=0;i<ASSIGN_DIMS && d->dim[i]>0;i++) ;
return i;
}
void *ElementPtr(Description *d,long index)
/* Returns NULL if PtrType array not allocated. */
{
char *ptr;
if(IsPtr(d->type)){
assert(d->ptr!=NULL);
ptr=*(void **)d->ptr;
}else ptr=d->ptr;
if(ptr!=NULL && d->dim[0]>0){
index+=d->firstElement;
ptr+=index*typeSize[d->type];
}
return ptr;
}
char *ElementName(char *s,Description *d,long index)
{
long sub[ASSIGN_DIMS];
int i;
ElementSubscript(d,index,sub);
sprintf(s,"%s",d->name);
for(i=0;i<Dims(d);i++)sprintf(s,"%s[%ld]",s,sub[i]);
return s;
}
char *DescriptionNameDimensions(Description *d);
char *DescriptionNameDimensions(Description *d)
{
int i;
static char s[64];
sprintf(s,"%s",d->name);
for(i=0;d->dim[i]>0;i++)sprintf(s,"%s[%ld]",s,d->dim[i]);
assert(strlen(s)<sizeof(s));
return s;
}
void ElementSubscript(Description *d,long index,long sub[ASSIGN_DIMS])
{
int i;
for(i=0;i<ASSIGN_DIMS;i++)sub[i]=0;
if(Dims(d)){
for(i=Dims(d)-1;i>=0;i--){
sub[i]=index%d->dim[i];
index/=d->dim[i];
}
sub[Dims(d)-1]+=d->firstElement;
}
}
long ElementIndex(Description *d,long sub[ASSIGN_DIMS])
{
int i;
long index=0;
if(Dims(d)){
sub[Dims(d)-1]-=d->firstElement;
for(i=0;i<Dims(d);i++){
index*=d->dim[i];
index+=sub[i];
}
sub[Dims(d)-1]+=d->firstElement;
}
return index;
}
int GetDimensions(Description *d,int subscriptDims,long subscript[ASSIGN_DIMS],short flags)
{
short i;
flags; /* dgp: prevent "unused argument" warning */
if(d->sizedOnce && Dims(d)!=subscriptDims)
return assignInconsistentDimensionsError;
if(!d->sized){
for(i=0;i<subscriptDims;i++)
if(d->dim[i]<subscript[i]+1)d->dim[i]=subscript[i]+1;
d->sizedOnce=1;
}
return 0;
}
int UnequalDescribedVarPair(Description *d1,Description *d2,short flags)
/*
Compares the data pointed to by the two Description structs and returns true if the
structs are legal and the data are equal, false otherwise. Comparison of floats
and doubles allows a tolerance of +/- one part in a million--because converting
to and from decimal may lose some precision--and ignores any NANs' indices (NAN04
vs NANFF)--because they're not preserved.
*/
{
unsigned char *p1,*p2;
long i,size,elements;
int error;
double a,b,e;
static const char here[]="UnequalDescribedVarPair";
char name[32+16*ASSIGN_DIMS];
/* Squeeze space out of names */
error=CheckADescription(d1,here,flags);
if(error<0)return error;
error=CheckADescription(d2,here,flags);
if(error<0)return error;
if(d1->type!=d2->type
|| !streq(d1->name,d2->name)
|| d1->firstElement!=d2->firstElement
|| !streq(d1->comment,d2->comment))
{error=assignInconsistentDescriptionsError;goto done;}
for(i=0;i<ASSIGN_DIMS;i++)if(d1->dim[i]!=d2->dim[i])
{error=assignInconsistentDescriptionsError;goto done;}
p1=ElementPtr(d1,0);
p2=ElementPtr(d2,0);
size=typeSize[d1->type];
elements=Elements(d1);
if(!IsString(d1->type)){
if(memcmp(p1,p2,size*elements)!=0){
if(!IsFloating(d1->type)){error=assignUnequalDataError;goto done;}
for(i=0;i<elements;i++){
switch(d1->type){
case floatType:
a=*(float *)p1;
b=*(float *)p2;
break;
#if SHORT_DOUBLE_OK
case shortDoubleType:
a=*(short double *)p1;
b=*(short double *)p2;
break;
#endif
case doubleType:
a=*(double *)p1;
b=*(double *)p2;
break;
}
/* compare doubles */
/* ignore NaN type, since it's not preserved */
if(IsNan(a)==0 || IsNan(b)==0){
if(!IsFinite(a) || !IsFinite(b))
{error=assignUnequalDataError;goto done;}
e=a/b-1.0;
if(e>TOLERANCE || e<-TOLERANCE)
{error=assignUnequalDataError;goto done;}
}
p1+=size;
p2+=size;
}
}
}else for(i=0;i<elements;i++){ /* compare strings */
if(*(char **)p1!=*(char **)p2){
if((*(char **)p1==NULL) || (*(char **)p2==NULL)
|| !streq(*(char **)p1,*(char **)p2))
{error=assignUnequalDataError;goto done;}
}
p1+=size;
p2+=size;
}
return 0;
done:
if(!(flags&assignNoPrintfExit))switch(error){
case assignInconsistentDescriptionsError:
PrintfExit("%s: the two Descriptions of \"%s\" are inconsistent.\n"
,here,d1->name);
break;
case assignUnequalDataError:
PrintfExit("%s: the two instances of \"%s\" have significantly different values.\n"
,here,ElementName(name,d1,i));
break;
}
return error;
}
int CopyDescribedVarPair(Description *d1,Description *d2,short flags)
/*
Copies the data pointed to by the first Description struct, overwriting the
data pointed to by the second struct. Returns 0 if successful, i.e. the structs
are legal and the types and names are consistent, nonzero otherwise.
*/
{
unsigned char *p1,*p2;
long i,size,elements;
int error;
static const char here[]="CopyDescribedVarPair";
/* Squeeze space out of names */
error=CheckADescription(d1,here,flags);
if(error<0)return error;
error=CheckADescription(d2,here,flags);
if(error<0)return error;
if(d1->type!=d2->type
|| !streq(d1->name,d2->name)
|| d1->firstElement!=d2->firstElement)
{error=assignInconsistentDescriptionsError;goto done;}
for(i=0;i<ASSIGN_DIMS;i++)if(d1->dim[i]!=d2->dim[i])
{error=assignInconsistentDescriptionsError;goto done;}
p1=ElementPtr(d1,0);
p2=ElementPtr(d2,0);
size=typeSize[d1->type];
elements=Elements(d1);
if(!IsString(d1->type))memcpy(p2,p1,size*elements);
else{
for(i=0;i<elements;i++){ /* copy strings */
if(!d2->malloced)*(char **)p2=NULL;
*(char **)p2=realloc(*(char **)p2,1+strlen(*(char **)p1));
if(*(void **)p2==NULL){
if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes for \"%s\".\n"
,here,1+strlen(*(char **)p1),d2->name);
else return assignMemoryError;
}
strcpy(*(char **)p2,*(char **)p1);
p1+=size;
p2+=size;
}
d2->malloced=1;
}
return 0;
done:
if(!(flags&assignNoPrintfExit))switch(error){
case assignInconsistentDescriptionsError:
PrintfExit("%s: the two Descriptions of \"%s\" are inconsistent.\n"
,here,d1->name);
break;
}
return error;
}
void FreeAPtrDescription(Description *d,short flags)
{
int i;
flags; /* dgp: prevent "unused argument" warning */
if(IsPtr(d->type)){
if(d->malloced)free(*(void **)d->ptr);
*(void **)d->ptr=NULL;
for(i=0;i<ASSIGN_DIMS;i++)d->dim[i]=0;
d->sizedOnce=d->sized=d->malloced=0;
}
}
void FreeAStringDescription(Description *d,short flags)
{
static const char emptyString[]="";
flags; /* dgp: prevent "unused argument" warning */
if(IsString(d->type)){
if(d->malloced && !d->dim[0])free(*(char **)d->ptr);
*(const char **)d->ptr=emptyString;
d->malloced=0;
}
}
void FreeDescribedPtrVars(Description d[],short flags)
{
for(;d->type!=0;d++)FreeAPtrDescription(d,flags);
}
void FreeADescribedVar(Description *d,short flags)
{
FreeAPtrDescription(d,flags);
FreeAStringDescription(d,flags);
}
void FreeDescribedVars(Description d[],short flags)
{
for(;d->type!=0;d++)FreeADescribedVar(d,flags);
}
void KeepDescribedVars(Description d[],short flags)
{
flags; /* dgp: prevent "unused argument" warning */
for(;d->type!=0;d++)d->malloced=0;
}
void KeepADescribedVar(Description *d,short flags)
{
flags; /* dgp: prevent "unused argument" warning */
d->malloced=0;
}
long FindDescription(Description d[],void *ptr,short flags)
{
long i;
for(i=0;d[i].type!=0;i++)if(ptr==d[i].ptr)return i;
if(PRINTF_EXIT)PrintfExit("FindDescription: couldn't find your variable.\n");
return assignCouldntFindDescription;
}
long FindDescribedDim(Description d[],void *ptr,int i,short flags)
{
long n;
n=FindDescription(d,ptr,flags);
if(n<0)return n;
if(i<0 || i>=ASSIGN_DIMS)return 0;
return d[n].dim[i];
}
long InitializeDescribedVars(Description d[],short flags)
{
long j,n=0;
for(;d->type!=0;d++){
j=InitializeADescribedVar(d,flags);
if(j<0)return j;
else n+=j;
}
return n;
}
long InitializeADescribedVar(Description *d,short flags)
{
static const char here[]="InitializeADescribedVar";
int error;
error=CheckADescription(d,here,flags);
if(error)return error;
if(IsPtr(d->type) && !d->malloced) *(void **)d->ptr=NULL;
if(IsPtr(d->type) || d->dim[0]){
Description w;
long i,j,n=0,elements;
if(ElementPtr(d,0)==NULL)return n;
w=*d;
if(IsPtr(w.type))w.type+=charType-charPtrType;
for(i=0;i<ASSIGN_DIMS;i++)w.dim[i]=0;
w.sizedOnce=w.sized=1;
if(IsString(w.type))w.malloced=0;
w.firstElement=0;
elements=Elements(d);
for(i=0;i<elements;i++){
w.ptr=ElementPtr(d,i);
j=InitializeADescribedVar(&w,flags);
if(j<0)return j;
n+=j;
}
return n;
}
switch(d->type){
case charType:
case unsignedCharType:
*(char *)d->ptr=0;
break;
case shortType:
case unsignedShortType:
*(short *)d->ptr=0;
break;
case longType:
case unsignedLongType:
*(long *)d->ptr=0;
break;
case floatType:
*(float *)d->ptr=NAN;
break;
#if SHORT_DOUBLE_OK
case shortDoubleType:
*(short double *)d->ptr=NAN;
break;
#endif
case doubleType:
*(double *)d->ptr=NAN;
break;
case stringType:
FreeAStringDescription(d,flags);
break;
default:
/* it shouldn't be possible to arrive here */
return assignVariableError;
}
return 1;
}
long PrintAssignmentsToFile(const char *filename,Description d[],short flags)
{
long n=0;
FILE *stream;
stream=fopen(filename,"a");
if(stream==NULL)return assignFileError;
n=PrintAssignments(stream,d,flags);
fprintf(stream,NL); /* Add blank line to separate blocks */
fclose(stream);
return n;
}
long PrintAssignments(FILE *stream,Description d[],short flags)
{
Description *vStart=d;
long n=0,i;
while(d->type!=0){
i=PrintAnAssignment(stream,d,flags);
if(i>0)fprintf(stream,NL); /* new line */
if(i<0)return i;
n+=i;
d++;
}
return n;
}
long PrintAnAssignmentOfElement(FILE *stream,Description *d,long index,short flags)
{
Description w;
long n,i;
static const char here[]="PrintAnAssignmentOfElement";
if(index<0 || index>=Elements(d))return assignSubscriptBoundsError;
w=*d;
for(i=0;i<ASSIGN_DIMS;i++)w.dim[i]=0;
w.firstElement=0;
w.comment=NULL;
w.name=(char *)malloc(strlen(w.name)+16*Dims(d));
if(w.name==NULL){
if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
,here,(long)strlen(w.name)+16*Dims(d));
else return assignMemoryError;
}
ElementName(w.name,d,index);
w.ptr=ElementPtr(d,index);
if(IsPtr(w.type))w.type+=charType-charPtrType;
if(IsString(w.type))w.malloced=0;
/* Turn off checking, 'cause subscripted name is illegal. */
n=PrintAnAssignment(stream,&w,flags|assignAlreadyChecked);
free(w.name);
return n;
}
long PrintAnAssignment(FILE *stream,Description *d,short flags)
{
short hexEncode;
char *string,name[32+16*ASSIGN_DIMS];
static const char here[]="PrintAnAssignment";
long index,n=0,row;
int error;
static short lineLength;/* zeroed before printing array or string and after \n. */
Description w;
error=CheckADescription(d,here,flags);
if(error<0)return error;
if(ElementPtr(d,0)==NULL)return n;
if(IsPtr(d->type) || d->dim[0]){
if(Dims(d))row=d->dim[Dims(d)-1];
else row=1;
hexEncode=IsIntegral(d->type) && !(flags&assignNoHexInts)
&&(row>2+typeSize[d->type]);
hexEncode|=IsFloating(d->type)&&(flags&assignHexFloats);
if(hexEncode){
for(index=0;index<Elements(d);index+=row){
lineLength=0;
w=*d;
if(IsString(w.type))w.malloced=0;
if(row>1)w.dim[Dims(d)-1]=0;
ElementName(name,&w,index/row);
lineLength+=fprintf(stream,"%s=",name);
string=BinaryToHex(row*typeSize[d->type]
,ElementPtr(d,index),NULL);
if(string==NULL){
if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
,here,2L*row*typeSize[d->type]);
else return assignMemoryError;
}
PrintQuotedString(stream,&lineLength,string,"\"","\"");
free(string);
lineLength+=fprintf(stream,";");
if(index<Elements(d)-row)lineLength+=fprintf(stream,"\\" NL);
}
n=Elements(d);
}else{
long i,assignLength,oldLineLength,elements;
lineLength=0;
elements=Elements(d);
for(i=0;i<elements;i++){
oldLineLength=lineLength;
n+=PrintAnAssignmentOfElement(stream,d,i,flags);
assignLength=lineLength-oldLineLength;
if(lineLength+assignLength+1>=MAX_LINELENGTH && i<elements-1){
fprintf(stream,"\\" NL);
lineLength=0;
lineLength+=fprintf(stream," ");
}
}
}
if(d->comment!=NULL && lineLength+strcspn(d->comment,NL)+6>=MAX_LINELENGTH){
fprintf(stream,"\\" NL);
lineLength=0;
lineLength+=fprintf(stream," ");
}
goto comment;
}
assert(d->dim[0]==0);
if(IsString(d->type))lineLength=0;
fflush(stream);
lineLength+=fprintf(stream,"%s=",d->name);
fflush(stream);
n=1;
switch(d->type){
case charType:
lineLength+=fprintf(stream,"%ld;",(long)*(char *)d->ptr);
break;
case unsignedCharType:
lineLength+=fprintf(stream,"%lu;",(unsigned long)*(unsigned char *)d->ptr);
break;
case shortType:
lineLength+=fprintf(stream,"%ld;",(long)*(short *)d->ptr);
break;
case unsignedShortType:
lineLength+=fprintf(stream,"%lu;",(unsigned long)*(unsigned short *)d->ptr);
break;
case longType:
lineLength+=fprintf(stream,"%ld;",*(long *)d->ptr);
break;
case unsignedLongType:
lineLength+=fprintf(stream,"%lu;",*(unsigned long *)d->ptr);
break;
case floatType:
#if SHORT_DOUBLE_OK
case shortDoubleType:
#endif
case doubleType:
if(flags&assignHexFloats){
string=BinaryToHex(typeSize[d->type],d->ptr,NULL);
if(string==NULL){
if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
,here,2L*typeSize[d->type]);
else return assignMemoryError;
}
lineLength+=fprintf(stream,"\"%s\";",string);
free(string);
}else switch(d->type){
case floatType:
lineLength+=fprintf(stream,"%.*g;",FLT_DIG,*(float *)d->ptr);
break;
#if SHORT_DOUBLE_OK
case shortDoubleType:
lineLength+=fprintf(stream,"%.*g;",DBL_DIG,*(short double *)d->ptr);
break;
#endif
case doubleType:
lineLength+=fprintf(stream,"%.*g;",DBL_DIG,*(double *)d->ptr);
break;
}
break;
case stringType:
PrintQuotedString(stream,&lineLength,*(const char **)d->ptr,"\"","\"");
lineLength+=fprintf(stream,";");
break;
default:
/* It shouldn't be possible to arrive here. */
PrintfExit("PrintAnAssignment: oops. \"%s\" of type (%s) fell through a crack.\n"
,d->name,typeName[d->type]);
n=0;
}
comment:
if(d->comment!=NULL && !(flags&assignNoComment))
lineLength+=fprintf(stream," /* %s */",d->comment);
return n;
}
int AssignmentLineWasBlank(void)
{
extern int lineWasBlank;
return lineWasBlank;
}
long ReadAssignmentFile(const char *filename,Description d[],short flags)
{
long n=0;
FILE *stream;
static const char here[]="ReadAssignmentFile";
stream=fopen(filename,"r");
if(stream==NULL)return assignFileError;
n=ReadAssignmentStream(stream,d,flags);
fclose(stream);
return n;
#if 0
if(!(flags&assignLocalCall)){
/* First pass: scan for dimensions of unallocated Ptr variables.*/
ReadAssignmentFile(filename,d,flags|assignScan|assignLocalCall);
flags&=~assignScan;
flags|=assignLocalCall;
error=AllocateDescribedPtrVars(d,here,flags);
if(error)return error;
/* Second pass: read data again, including the newly allocated Ptr variables */
}
stream=fopen(filename,"r");
if(stream==NULL)return assignFileError;
do{
int i;
i=ReadAssignmentLine(stream,d,flags);
if(i>=0)n+=i;
else{
n=i;
break;
}
}while(!feof(stream));
fclose(stream);
return n;
#endif
}
long ReadAssignmentStream(FILE *stream,Description d[],short flags)
{
long n=0;
int error;
static const char here[]="ReadAssignmentStream";
if(!(flags&assignLocalCall)){
/* First pass: scan for dimensions of unallocated Ptr variables.*/
long position=ftell(stream);
ReadAssignmentStream(stream,d,flags|assignScan|assignLocalCall);
flags&=~assignScan;
flags|=assignLocalCall;
fseek(stream,position,SEEK_SET);
error=AllocateDescribedPtrVars(d,here,flags);
if(error)return error;
/* Second pass: read data again, including the newly allocated Ptr variables */
}
do{
int i;
i=ReadAssignmentLine(stream,d,flags);
if(i>=0)n+=i;
else{
n=i;
break;
}
}while(!feof(stream));
return n;
}
long ReadAssignmentBlock(FILE *stream,Description d[],short flags)
{
long n=0;
int error;
static const char here[]="ReadAssignmentBlock";
if(!(flags&assignLocalCall)){
/* First pass: scan for dimensions of unallocated Ptr variables.*/
long position=ftell(stream);
ReadAssignmentBlock(stream,d,flags|assignScan|assignLocalCall);
flags&=~assignScan;
flags|=assignLocalCall;
fseek(stream,position,SEEK_SET);
error=AllocateDescribedPtrVars(d,here,flags);
if(error)return error;
/* Second pass: read data again, including the newly allocated Ptr variables */
n=ReadAssignmentBlock(stream,d,flags);
return n;
}
do{
int i;
i=ReadAssignmentLine(stream,d,flags);
if(i>=0)n+=i;
else{
n=i;
break;
}
}while(!AssignmentLineWasBlank());
return n;
}
static char *lineBuffer; /* global because it would be messy to pass this address to
all the routines below that need to call ParsingError */
long ReadAssignmentLine(FILE *stream,Description description[],short flags)
{
register Description *d;
Description *vMatch;
long j,n=0,nAssign;
int i;
char *s,*sOld;
long ftellOld;
void *ptr;
extern int lineWasBlank;
int hexAllowed,subscriptDims,outOfBounds,unknown;
long subscript[ASSIGN_DIMS],index;
int error;
Description scratchDescription,*dScratch;
double scratchDouble;
long scratchLong;
void *scratchPtr;
static const char here[]="ReadAssignmentLine";
if(!(flags&assignLocalCall)){
/* First pass: scan for dimensions of unallocated Ptr variables.*/
long position=ftell(stream);
ReadAssignmentLine(stream,description,flags|assignScan|assignLocalCall);
flags&=~assignScan;
flags|=assignLocalCall;
fseek(stream,position,SEEK_SET);
error=AllocateDescribedPtrVars(description,here,flags);
if(error)return error;
/* Second pass: read data again, including the newly allocated Ptr variables */
}
if(stream==NULL)return 0;
error=CheckDescriptions(description,here,flags);
if(error)return error;
lineWasBlank=TRUE;
lineBuffer=(char *)malloc(BUFFER_SIZE);
if(lineBuffer==NULL){
if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
,here,(long)BUFFER_SIZE);
else return assignMemoryError;
}
dScratch=&scratchDescription;
s=NULL;
do{
/* parse the name */
if(s==NULL)
s=NextTokenInThisOrNextLine(stream,lineBuffer,s,flags&~assignNeedMore);
else s=NextTokenInLine(stream,lineBuffer,s,flags);
if(s==NULL){
free(lineBuffer);
return n;
}
*dScratch=Describe(unknownType,&scratchPtr,GetName(&s,flags),NULL);
if(dScratch->name==NULL){
free(lineBuffer);
return assignMemoryError;
}
dScratch->sizedOnce=dScratch->sized=dScratch->malloced=0;
d=dScratch;
unknown=1;
for(vMatch=&description[0];vMatch->type!=0;vMatch++){
if(streq(dScratch->name,vMatch->name)){
d=vMatch;
unknown=0;
break;
}
}
if(unknown && (flags&assignReportUnknown)){
if(PRINTF_EXIT)ParsingError(s,"unknown variable");
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return assignUnknownVariableError;
}
}
hexAllowed=IsIntegral(d->type)&&!(flags&assignNoHexInts)
|| IsFloating(d->type)&&(flags&assignHexFloats);
s=NextToken(stream,lineBuffer,s,flags);
subscriptDims=0;
for(i=0;i<ASSIGN_DIMS;i++)subscript[i]=0;
outOfBounds=0;
if(IsPtr(d->type) && !d->malloced)outOfBounds=1;
/* parse subscripts, e.g. [123][3] */
for(i=0;i<Dims(d) || !d->sized;i++){
/* parse a subscript, e.g. [123] */
if(*s != '['){
if(hexAllowed && subscriptDims==Dims(d)-1)break;
if(!d->sized)break;
if(PRINTF_EXIT)ParsingError(s,"expected \"[\"");
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return assignSubscriptError;
}
}
if(i==ASSIGN_DIMS){
if(PRINTF_EXIT)ParsingError(s,"too many dimensions");
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return assignSubscriptError;
}
}
s++;
s=NextToken(stream,lineBuffer,s,flags);
sOld=s;
ftellOld=ftell(stream);
subscript[i]=strtolN(s,&s,0);
subscriptDims=i+1;
if(s==sOld && ftellOld==ftell(stream)){
if(PRINTF_EXIT)ParsingError(s,"expected subscript");
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return assignSubscriptError;
}
}
j=subscript[i];
if(i==Dims(d)-1)j-=d->firstElement;
if(j<0 || d->sized && j>=d->dim[i]){
if(flags&assignReportUnknown){
if(PRINTF_EXIT)ParsingError(s,"subscript out of bounds: %s"
,DescriptionNameDimensions(d));
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return assignSubscriptBoundsError;
}
}
outOfBounds=1;
}
s=NextToken(stream,lineBuffer,s,flags);
if(*s != ']'){
if(PRINTF_EXIT)ParsingError(s,"expected \"]\"");
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return assignSubscriptError;
}
}
s++;
s=NextToken(stream,lineBuffer,s,flags);
}
/* parse the equal sign */
if(*s != '='){
if(PRINTF_EXIT)ParsingError(s,"expected \"=\"");
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return assignEqualsError;
}
}
s++;
/* parse the value */
s=NextToken(stream,lineBuffer,s,flags);
if(d->sized && Dims(d)!=subscriptDims
&& !(*s=='"' && Dims(d)-1==subscriptDims)){
if(PRINTF_EXIT)
ParsingError(s,"\"%s\": wrong number of subscripts",DescriptionNameDimensions(d));
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return assignSubscriptError;
}
}
if(d->type==unknownType){
/* choose type that can accept the data, so we can parse and discard it. */
if(*s=='"')d->type=stringType;
else if(streq2(s,"0x") || streq2(s,"0X") || *s=='\''
|| !(flags&assignNoHexInts) && *s=='"')d->type=longType;
else d->type=doubleType;
}
if(unknown || outOfBounds){
/* use scratch scalar to receive the assignment */
short type=d->type;
if(IsPtr(type))type+=charType-charPtrType;
if(1){
/* 11/8/95 new code. Retain the allocated dScratch->name */
if(IsString(type))
*dScratch=Describe(type,&scratchPtr,dScratch->name,NULL);
else if(IsIntegral(type))
*dScratch=Describe(type,&scratchLong,dScratch->name,NULL);
else if(IsFloating(type))
*dScratch=Describe(type,&scratchDouble,dScratch->name,NULL);
}else{
/* old code. Lost the allocated dScratch->name, which wreaked havoc when we later free() it. */
if(IsString(type))
*dScratch=Describe(type,&scratchPtr,d->name,NULL);
else if(IsIntegral(type))
*dScratch=Describe(type,&scratchLong,d->name,NULL);
else if(IsFloating(type))
*dScratch=Describe(type,&scratchDouble,d->name,NULL);
}
d=dScratch;
index=0;
/* Note: d is now inconsistent with subscript and subscriptDims */
}else index=ElementIndex(d,subscript);
ptr=ElementPtr(d,index);
sOld=s;
ftellOld=ftell(stream);
nAssign=1;
error=0;
if(*s=='"' && !IsString(d->type) && hexAllowed){
long bytes,elements;
if(unknown || outOfBounds)
nAssign=HexAssignment(stream,lineBuffer,&s,d,0,index,&bytes,flags);
else nAssign=HexAssignment(stream,lineBuffer,&s,d,subscriptDims,index
,&bytes,flags);
if(!unknown){
elements=bytes/typeSize[vMatch->type];
if(elements>1){
subscript[subscriptDims++]=elements-1;
error=GetDimensions(vMatch,subscriptDims,subscript,flags);
subscript[--subscriptDims]=0;
}else error=GetDimensions(vMatch,subscriptDims,subscript,flags);
}
}else{
if(!unknown)error=GetDimensions(vMatch,subscriptDims,subscript,flags);
else error=0;
if(!error)switch(d->type){
default:
assert(0 /* It shouldn't be possible to arrive here. */);
nAssign=0;
break;
case charType:
case charPtrType:
*(char *)ptr=strtolN(s,&s,0);
break;
case unsignedCharType:
case unsignedCharPtrType:
*(unsigned char *)ptr=strtoulN(s,&s,0);
break;
case shortType:
case shortPtrType:
*(short *)ptr=strtolN(s,&s,0);
break;
case unsignedShortType:
case unsignedShortPtrType:
*(unsigned short *)ptr=strtoulN(s,&s,0);
break;
case longType:
case longPtrType:
*(long *)ptr=strtolN(s,&s,0);
break;
case unsignedLongType:
case unsignedLongPtrType:
*(unsigned long *)ptr=strtoulN(s,&s,0);
break;
case floatType:
case floatPtrType:
*(float *)ptr=strtodN(s,&s);
break;
#if SHORT_DOUBLE_OK
case shortDoubleType:
case shortDoublePtrType:
*(short double *)ptr=strtodN(s,&s);
break;
#endif
case doubleType:
case doublePtrType:
*(double *)ptr=strtodN(s,&s);
break;
case stringType:
if(d->malloced && !d->dim[0])free(*(char **)ptr);
*(char **)ptr=GetQuotedString(stream,lineBuffer,s,&s,flags);
d->malloced=1;
break;
}
}
if(error==assignInconsistentDimensionsError){
if(PRINTF_EXIT)ParsingError(s,"inconsistent dimensionality of \"%s\""
,DescriptionNameDimensions(d));
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return error;
}
}
if(s==sOld && ftellOld==ftell(stream)){
if(PRINTF_EXIT)ParsingError(s,"expected %s",typeName[d->type]);
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return assignConstantError;
}
}
/* parse the semicolon */
s=NextToken(stream,lineBuffer,s,flags);
if(*s!=';'){
if(PRINTF_EXIT)ParsingError(s,"expected \";\"");
else {
free(dScratch->name); /* 7/16/94 */
free(lineBuffer);
return assignSemicolonError;
}
}
s++;
if(ECHO_ASSIGNMENTS){
if(unknown)printf("/*UNKNOWN: ");
else if(outOfBounds)printf("/*OUT OF BOUNDS: ");
if(!subscriptDims)PrintAnAssignment(stdout,d
,(flags&~assignHexFloats)|assignNoComment);
else PrintAnAssignmentOfElement(stdout,d,index
,(flags&~assignHexFloats)|assignNoComment);
if(outOfBounds || unknown)printf("*/ ");
}
free(dScratch->name); /* 7/16/94 */
dScratch->name=NULL;
FreeADescribedVar(dScratch,flags);
if(d!=dScratch)n+=nAssign;
} while(1);
}
char *GetName(char **sPtr,short flags)
/* Gets a variable's name. */
{
char *s,*sEnd,*name;
long bytes=0,i;
static const char here[]="ReadAssignmentLine/CheckADescription:GetName";
s=*sPtr;
if(ParseName(&s))if(PRINTF_EXIT)ParsingError(s,"expected name");
sEnd=s;
bytes=0;
for(s=*sPtr;!isspace(*s) && s!=sEnd;s++)bytes++;
name=(char *)malloc(bytes+1);
if(name==NULL){
if(PRINTF_EXIT)ParsingError(s,"%s: no room for %ld bytes.\n"
,here,bytes+1);
else return NULL;
}
for(i=0;i<bytes;i++){
while(isspace((*sPtr)[i]))(*sPtr)++;
name[i]=(*sPtr)[i];
}
name[bytes]=0;
*sPtr=s;
return name;
}
int ParseName(char **sPtr)
/*
Parse a variable's name. It may be just a C identifier: alphabetic (or _)
followed by any number of alphanumeric (or _). Or it may be made up of multiple
identifiers joined by "." or "->" infix operators, e.g. "LP->p".
*/
{
char *s;
int error=0;
s=*sPtr;
while(*s!=0){
for(;isspace(*s);s++) ;
if(!(isalpha(*s)||*s=='_')){
error=1;
break;
}
for(;isalnum(*s)||*s=='_';s++) ;
for(;isspace(*s);s++) ;
if(*s=='.'){
s++;
continue;
}
if(streq2(s,"->")){
s+=2;
continue;
}
break;
}
*sPtr=s;
return error;
}
long HexAssignment(FILE *stream,char *lineBuffer,char **sPtr,Description *d
,int subscriptDims,long index,long *bytesPtr,short flags)
{
char *string,*s;
long bytes,excess,n;
int error;
void *ptr;
if(!(flags&assignScan)
&& (subscriptDims<Dims(d)-1 || subscriptDims>Dims(d)))return assignSubscriptError;
ptr=ElementPtr(d,index);
/*
Read in hex data.
For scalar insist on exactly the right number of bytes.
For array, if flags&assignReportUnknown then insist on exactly the right number
of bytes; otherwise merely insist that the binary object size be a
multiple of the data element size.
*/
s=*sPtr;
string=GetQuotedString(stream,lineBuffer,s,&s,flags);
*bytesPtr=strlen(string)/2;
bytes=typeSize[d->type];
if(subscriptDims==Dims(d)-1)bytes*=d->dim[subscriptDims];
excess=strlen(string)-2*bytes;
if(subscriptDims==Dims(d)-1 && !(excess>0 && (flags&assignReportUnknown)))
excess=strlen(string)%(2*typeSize[d->type]);
if(excess!=0 && !(flags&assignScan)){
free(string);
if(PRINTF_EXIT){
if(excess>0)ParsingError(s,"\"%s=\", %ld extra hex digits",d->name,excess);
else ParsingError(s,"\"%s=\", %ld too few hex digits",d->name,-excess);
}else return assignHexError;
}
string[strlen(string)-strlen(string)%(2*typeSize[d->type])]=0;
if(strlen(string)>2*bytes)string[2*bytes]=0;
if(ptr!=NULL){
error=HexToBinary(string,ptr);
if(error){
if(PRINTF_EXIT)ParsingError(s,"\"%s=\", hex string contains non hex char:\n\"%s\"\n"
,d->name,string);
else {
free(string);
return assignHexError;
}
}
n=strlen(string)/(2*typeSize[d->type]); /* number of assigments */
}else n=0;
free(string);
*sPtr=s;
return n;
}
char *GetQuotedString(FILE *stream,char *lineBuffer,char *s,char **sPtr,short flags)
/*
Returns a newly malloced string copied from the lineBuffer (starting at s),
supplemented if necessary, by reading in further lines from the stream. The
translation from input to output mimics what would be performed by a C compiler.
Each line of input must fit in the BUFFER_SIZE byte lineBuffer, but the
resulting string (concatenated across continuations and adjacent strings) can be
of any length, limited only by how much space can be obtained by malloc. Returns
NULL if an error occurs (e.g. can't malloc needed space).
*/
{
char *newS,*oldString,*newString;
size_t stringSize;
long j;
static const char here[]="GetQuotedString";
/* parse the opening quote mark */
if(*s!='"'){
if(PRINTF_EXIT)ParsingError(s,"expected '\"'");
else return NULL;
}
s++;
stringSize=0;
newS=newString=NULL;
do{
/* copy string, translating any backslash escapes */
while(1){
j=strcspn(s,"\\\"");
if(stringSize==0 || strlen(newString)+j+2>stringSize){
/* allocate a string that's big enough */
stringSize+=j;
stringSize*=2;
if(stringSize<BUFFER_SIZE)stringSize=BUFFER_SIZE;
oldString=newString;
newString=malloc(stringSize);
if(newString==NULL){
if(oldString!=NULL)free(oldString);
if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
,here,(long)stringSize);
else return NULL;
}
if(oldString!=NULL){
strcpy(newString,oldString);
free(oldString);
}
newS+=newString-oldString;
}
strncpy(newS,s,j);
s+=j;
newS+=j;
*newS=0;
if(streq2(s,"\\" NL)){
s+=2;
continue;
}
if(*s=='\\'){
*newS++=strtoc(s,&s);
*newS=0;
}else break;
};
if(strlen(s)==0){
s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags|assignNeedMore);
continue;
}
/* parse the closing quote mark */
if(*s!='"'){
if(PRINTF_EXIT)ParsingError(s,"expected '\"'");
else{
free(newString);
return NULL;
}
}
s++;
/* look for another opening quote mark. Concatenate adjacent strings */
s=NextToken(stream,lineBuffer,s,flags);
if(*s!='"')break;
s++;
}while(1);
/* shrink string allocation down to what we actually used */
stringSize=1+strlen(newString);
newString=realloc(newString,stringSize);
if(newString==NULL){
if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
,here,(long)stringSize);
else return NULL;
}
*sPtr=s;
return newString;
}
void ParsingError(char *s,char *format,...)
{
va_list args;
long i;
static short lineLength=0;/* zeroed after each \n. */
printf("\007\nReadAssignmentLine: ");
va_start(args,format);
vfprintf(stdout,format,args);
va_end(args);
if(s==NULL)PrintfExit(".\n");
printf(". Quitting at |.\n");
if(0){
printf("%s",lineBuffer);
if(lineBuffer[strlen(lineBuffer)-1]!=NEWLINE)printf("...\n");
}
if(lineBuffer[strlen(lineBuffer)-1]==NEWLINE)lineBuffer[strlen(lineBuffer)-1]=0;
PrintQuotedString(stdout,&lineLength,lineBuffer,"\"","\"\n");
s[0]='|';
s[1]=0;
s=lineBuffer;
for(i=0;i<strlen(s)-1;i++)if(isgraph(s[i]))s[i]=' ';
if(0)PrintfExit("%s\n",lineBuffer);
PrintQuotedString(stdout,&lineLength,lineBuffer," ","\n");
PrintfExit("");
}
char *ReadLine(char *lineBuffer,size_t bufferSize,FILE *stream,short flags)
/* The Boolean global lineWasBlank is initialized to true in ReadAssignmentLine(), */
/* falsified (if appropriate) here, and returned by AssignmentLineWasBlank(). */
{
char *s;
static const char whiteSpace[]=" \t\n\r\v\f";
extern int lineWasBlank;
s=fgets(lineBuffer,bufferSize,stream);
if(s==NULL){
if(flags&assignNeedMore){
if(PRINTF_EXIT)ParsingError(s,"premature end of stream");
else return NULL;
}else return NULL;
}
if(strlen(s)!=strspn(s,whiteSpace))lineWasBlank=FALSE;
if(strlen(s)>=bufferSize-1 && s[strlen(s)-1]!=NEWLINE){
if(PRINTF_EXIT){
s+=strlen(s);
ParsingError(s,"line exceeds buffer size");
}else return NULL;
}
if(ECHO_FILE)printf("%s",s);
return s;
}
char *NextToken(FILE *stream,char *lineBuffer,char *s,short flags)
/*
Skip past white space and comments to the beginning of the next token,
reading as many lines as necessary.
*/
{
do{
s=NextTokenInThisOrNextLine(stream,lineBuffer,s,flags|assignNeedMore);
if(s!=NULL || feof(stream))return s;
}while(1);
}
char *NextTokenInThisOrNextLine(FILE *stream,char *lineBuffer,char *s,short flags)
/*
Skip past white space and comments to the beginning of the next token,
reading one line if necessary.
*/
{
s=NextTokenInLine(stream,lineBuffer,s,flags);
if(s!=NULL)return s;
s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags);
s=NextTokenInLine(stream,lineBuffer,s,flags);
return s;
}
char *NextTokenInLine(FILE *stream,char *lineBuffer,char *s,short flags)
/*
Skip past white space and comments to the beginning of the next token. NextTokenInLine
will read in new lines only if they are continuations of the current line (i.e.
it ends in "\") or if new lines are needed to find the end of an unfinished
comment. Input from stream is buffered one line at a time in lineBuffer, and s
points to the next unparsed character in that buffer.
*/
{
static const char whiteSpace[]=" \t\n\r\v\f";
char *sTemp;
do{
/* skip white space and comments */
do {
if(s==NULL || strlen(s)==0)goto endOfLine;
s+=strspn(s,whiteSpace);
if(strlen(s)==0)goto endOfLine;
if(streq2(s,"/*")){
do{
sTemp=s;
s=strstr(s,"*/");
if(s!=NULL)break;
if(ECHO_COMMENTS)printf("%s",sTemp);
s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags|assignNeedMore);
} while(1);
s+=2;
if(ECHO_COMMENTS){
s[-1]=0;
printf("%s/",sTemp);
}
continue;
}
if(streq2(s,"//")){
if(ECHO_COMMENTS){
if(s[strlen(s)-1]==NEWLINE)s[strlen(s)-1]=0;
printf("%s",s);
}
goto endOfLine;
}
if(streq2(s,"\\" NL)){
if(ECHO_COMMENTS || ECHO_ASSIGNMENTS)printf(" \\\n");
s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags&~assignNeedMore);
continue;
}
break;
} while(1);
return s; /* normal return */
}while(1);
endOfLine:
if((ECHO_ASSIGNMENTS || ECHO_COMMENTS) && s!=NULL)printf("\n");
return NULL;
}
double strtodN(char *s,char **sPtr)
/*
Supplement standard C routine strtod() by also handling NAN and INF. Any specification
of the NAN's type (1 to 255) is ignored, whether in THINK C style, e.g. NANFF, in
MPW C style, e.g. Nan[255], or Metrowerks CodeWarrior C style, e.g. NAN(021).
*/
{
if(streq2(s,"NAN")||streq2(s,"NaN")||streq2(s,"Nan")||streq2(s,"nan")){
s+=3;
s+=strspn(s,"0123456789abcdefABCDEF[]()");
*sPtr=s;
return NAN;
}
if(strncmp(s,"INF",3)==0||strncmp(s,"Inf",3)==0||strncmp(s,"inf",3)==0){
s+=3;
*sPtr=s;
return INF;
}
if(strncmp(s,"-INF",4)==0||strncmp(s,"-Inf",4)==0||strncmp(s,"-inf",4)==0){
s+=4;
*sPtr=s;
return -INF;
}
return strtod(s,sPtr);
}
long strtolN(char *s,char **sPtr,short flags)
/* Supplement standard C routine strtol() by handling quoted char, e.g. 'e' */
{
long j;
if(*s!='\'') return strtol(s,sPtr,flags);
s++;
j=strtoc(s,&s);
if(*s!='\''){
if(PRINTF_EXIT)ParsingError(s,"expected \"'\"");
else return 0;
}
*sPtr=s+1;
return j;
}
unsigned long strtoulN(char *s,char **sPtr,short flags)
/* Supplement standard C routine by handling quoted char, e.g. 'e' */
{
unsigned long j;
if(*s!='\'') return strtoul(s,sPtr,flags);
s++;
j=strtoc(s,&s);
j &= 255L; /* strip off any sign extension, since char was signed */
if(*s!='\''){
if(PRINTF_EXIT)ParsingError(s,"expected \"'\"");
else return 0;
}
*sPtr=s+1;
return j;
}
char strtoc(char *s,char **sPtr)
/* Extract a character from a string, translating backslash escapes */
{
char c;
startAgain:
if(*s!='\\'){
if(*s==0){
*sPtr=s;
return -1;
}
*sPtr=s+1;
return *s;
}
s++;
switch(s++[0]){
case 'n': c='\n'; break;
case 'r': c='\r'; break;
case 't': c='\t'; break;
case 'b': c='\b'; break;
case 'v': c='\v'; break;
case 'f': c='\f'; break;
case 'a': c='\a'; break;
case 'x': c=strtol(s,&s,16); break;
case '0': case '1':case '2': case '3':case '4': case '5':case '6': case '7':
c=strtol(s-1,&s,8); break;
case NEWLINE: s++; goto startAgain;
default: c=s[-1]; break;
}
*sPtr=s;
return c;
}
void PrintQuotedString(FILE *stream,short *lineLengthPtr,const char *s
,const char *openQuote,const char *closeQuote)
{
*lineLengthPtr+=fprintf(stream,openQuote);
do{
for(;*s!=0 && *lineLengthPtr+1+(isprint(*s)?1:(isspace(*s)?2:4))<=MAX_LINELENGTH;s++){
if(isprint(*s) && *s!='"' && *s!='\\')
*lineLengthPtr+=fprintf(stream,"%c",(PRINTF_INT)*s);
else switch(*s){
case '"': *lineLengthPtr+=fprintf(stream,"\\\""); break;
case '\\': *lineLengthPtr+=fprintf(stream,"\\\\"); break;
case '\n': *lineLengthPtr+=fprintf(stream,"\\n"); break;
case '\t': *lineLengthPtr+=fprintf(stream,"\\t"); break;
case '\b': *lineLengthPtr+=fprintf(stream,"\\b"); break;
case '\r': *lineLengthPtr+=fprintf(stream,"\\r"); break;
case '\v': *lineLengthPtr+=fprintf(stream,"\\v"); break;
case '\f': *lineLengthPtr+=fprintf(stream,"\\f"); break;
case '\312': *lineLengthPtr+=fprintf(stream,"\312"); break; /* option-space */
default: *lineLengthPtr+=fprintf(stream,"\\%03lo"
,(unsigned long)*(unsigned char *)s); break;
}
}
if(*s!=0){
fprintf(stream,"\"" NL);
*lineLengthPtr=0;
*lineLengthPtr+=fprintf(stream," \"");
}
}while(*s!=0);
*lineLengthPtr+=fprintf(stream,closeQuote);
}
#if 0
/* A longer, Macintosh-savvy, version of this routine, PrintfExit.c, */
/* is part of the VideoToolbox. */
int PrintfExit(const char *format,...)
{
va_list args;
int i;
va_start(args,format);
i=vfprintf(stdout,format,args);
va_end(args);
exit(1);
}
#endif